package org.example.searcher;

import java.io.*;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Parser {
    // 指定一个加载文档的路径
    private static final String INPUT_PATH = "D:/java_develop_doucement/jdk-17.0.11_doc-all/docs/api";
    // 创建一个Index实例
    private Index index = new Index();

    // 使用单线程制作索引
    public void run() {

        System.out.println("索引制作开始！");
        long begin = System.currentTimeMillis();
        // 整个Parse类的入口
        // 1. 根据上述指定的路径，枚举出该路径中所有的文件(html)，这个过程需要把所有的子目录中的文件都能获取到
        ArrayList<File> fileList = new ArrayList<>();
        enumFile(INPUT_PATH, fileList);

        // 2. 针对上面罗列出的文件的路径，打开文件，读取文件内容，并进行解析，并构建索引
        for (File file : fileList) {
            System.out.println("开始解析：" + file.getAbsolutePath());
            parseHTML(file);
        }

        // 3. 把在内存中构建好的索引数据结构，保存到指定的文件中
        index.save();
        long end = System.currentTimeMillis();
        System.out.println("索引制作完成！消耗时间：" + (end - begin) + "ms");
    }

    // 使用多线程制作索引
    public void runByThread(){
        System.out.println("索引制作开始！");
        long begin = System.currentTimeMillis();

        // 1. 枚举出所有文件
        ArrayList<File> files = new ArrayList<>();
        enumFile(INPUT_PATH, files);

        // 2. 循环遍历文件，此处为了通过多线程制作索引，直接引入线程池
        CountDownLatch latch = new CountDownLatch(files.size()); // 用来判断所有文件是否都解析完毕
        ExecutorService executorService = Executors.newFixedThreadPool(10); // 构建线程池
        for (File file : files) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("解析：" + file.getAbsolutePath());
                    parseHTML(file);
                    latch.countDown(); // 一个文件解析完成后进行撞线
                }
            });
        }
        try {
            // 进入阻塞，直到所有文件都调用countDown撞线之后，阻塞才会结束
            latch.await();
            // 手动把线程池里面的线程干掉
            executorService.shutdown();
            // 3. 把在内存中构建好的索引数据结构，保存到指定的文件中
            index.save();
            long end = System.currentTimeMillis();
            System.out.println("索引制作完成！消耗时间：" + (end - begin) + "ms");

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    }

    private void parseHTML(File file) {

        // 1. 解析出 HTML 的标题
        String title = parseTitle(file);
        // 2. 解析出 HTML 的链接
        String url = parseUrl(file);
        // 3. 解析出 HTML 的正文
        String content = parseContentByRegex(file);
        // 4. 把解析出来的这些信息加入到索引类中
        index.addDoc(title, url, content);
    }

    // 未使用正则表达式
    public String parseContent(File file) {

        // 先按照一个字符一个字符的方式来读取，以 < 和 > 来控制拷贝数据的开关
        // 手动把缓冲区设置为 1M 大小
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file), 1024 * 1024)){
            // 判断是否进行拷贝的开关
            boolean isCopy = true;
            // content用来保存结果
            StringBuilder content = new StringBuilder();
            while (true) {

                int ret = bufferedReader.read(); // 这里的返回值为int类型，若文件到了末尾则返回-1，否则返回对应字符的ASCII码
                if (ret == -1) {
                    break;
                }
                char c = (char)ret;
                if (isCopy) {
                    if (c == '<') {
                        isCopy = false;
                        continue;
                    }
                    if (c == '\n' || c == '\r') {
                        // 若为换行，将c换成空格
                        c = ' ';
                    }
                    // 其它字符可以直接拷贝
                    content.append(c);
                }
                else {
                    if (c == '>') {
                        isCopy = true;
                    }
                }
            }
            return content.toString();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private String readFile(File f) {
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(f))){
            StringBuilder content = new StringBuilder();
            while (true) {
                int ret = bufferedReader.read();
                if (ret == -1) {
                    break;
                }
                char c = (char) ret;
                if (ret == '\n' || ret == '\r') {
                    c = ' ';
                }
                content.append(c);
            }
            return content.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    // 这个方法内部就基于正则表达式，实现去标签，以及去除script
    private String  parseContentByRegex(File f) {
        // 1. 先把整个文件都读到String里
        String content = readFile(f);
        // 2. 替换掉 script 标签
        content = content.replaceAll("<script.*?>(.*?)</script>", " ");
        // 3. 替换掉普通的html标签
        content = content.replaceAll("<.*?>", " ");
        // 4. 把多个空格合并成一个空格
        content = content.replaceAll("\\s+", " ");
        return content;
    }

    private String parseUrl(File file) {
        String part1 = "https://docs.oracle.com/en/java/javase/17/docs/api/";
        String part2 =  file.getAbsolutePath().substring(INPUT_PATH.length());
        return part1 + part2;
    }

    private String parseTitle(File file) {
        String name = file.getName();
        return name.substring(0, name.length() - ".html".length());
    }

    // 第一个参数表示从哪个目录开始进行递归遍历
    // 第二个参数表示递归得到的结果
    private void enumFile(String inputPath, ArrayList<File> fileList) {

        // 根目录
        File rootPath = new File(inputPath);

        // listFiles能够获取到当前目录下的所有子文件
        File[] files = rootPath.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                // 如果当前文件为目录，则递归该目录，获取该目录下的子文件
                enumFile(file.getAbsolutePath(), fileList);
            }
            else {
                // 若当前文件非目录，则直接添加进返回结果 (只添加html)
                if (file.getAbsolutePath().endsWith(".html")) {
                    fileList.add(file);
                }

            }
        }

    }

    public static void main(String[] args) {
        // 1. 通过main来实现整个制作索引过程
        Parser parser = new Parser();
        parser.runByThread();
    }
}
